﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.DataVisualization.Charting;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using GraphEntities;
using NumericalData.Interpolation.Data;
using NumericalData.Interpolation.Data.Methods;
using WPFSolution.CommonOperations;
using Point = GraphEntities.Point;

namespace WPFSolution.UserControls
{
    /// <summary>
    /// Interaction logic for FunctionsDrawer.xaml
    /// </summary>
    public partial class FunctionsDrawer : UserControl
    {
        #region Fields

        private ExpressionContainer _innerExpression;
        private InterpolatedFunction _function;
        private InterpolationMethod _method; 

        private Style _natDataPointStyle;
        private Style _interpolDataPointStyle;
        #endregion

        #region Dependency Properties

        #region ExpressionName

        public static readonly DependencyProperty ExpressionNameProperty =
            DependencyProperty.Register("ExpressionName", typeof(string), typeof(FunctionsDrawer), new PropertyMetadata(default(string)));

        public string ExpressionName
        {
            get { return (string)GetValue(ExpressionNameProperty); }
            set { SetValue(ExpressionNameProperty, value); }
        }
        #endregion

        #region ValuesGrid

        public static readonly DependencyProperty ValuesGridProperty =
            DependencyProperty.Register("ValuesGrid", typeof(EquidistantPointsGrid), typeof(FunctionsDrawer), new PropertyMetadata(default(EquidistantPointsGrid)));

        public EquidistantPointsGrid ValuesGrid
        {
            get { return (EquidistantPointsGrid)GetValue(ValuesGridProperty); }
            set { SetValue(ValuesGridProperty, value); }
        }
        #endregion

        #endregion

        #region Constructors

        public FunctionsDrawer(ExpressionContainer exp, InterpolationMethod method)
        {
            InitializeComponent();
            InitializeLinesStyles();
            if (exp != null && exp.Grid != null)
            {
                _method = method;
                _function = _method.ExecuteFunction(exp);
                BuildGraphics(exp);
            }
            else
            {
                ValuesGrid = null;
                ExpressionName = "No Data To Show";
            }
        }

        #endregion

        #region Private Methods

        private void InitializeLinesStyles()
        {
            Style dataPointStyle = (Style)FindResource("chartLineDataEmptyPoint");
            _natDataPointStyle =
                new Style(typeof(LineDataPoint), dataPointStyle);
            _natDataPointStyle.Setters.Add(new Setter(BackgroundProperty, Brushes.Green));

            _interpolDataPointStyle =
                new Style(typeof(LineDataPoint), dataPointStyle);
            _interpolDataPointStyle.Setters.Add(new Setter(BackgroundProperty, Brushes.Red));
        }

        private void BuildGraphics(ExpressionContainer exp)
        {
                _innerExpression = exp;
                ValuesGrid = exp.Grid;
                ExpressionName = exp.Expression;
                BuildInterpolatedByGrid(exp);
                BuildExpandedGraphic(exp, 5);
        }

        private void BuildInterpolatedByGrid(ExpressionContainer exp)
        {
            var calculatablePoints = GetCalculateablePointsList(exp.Grid.Values.PointList, exp.MethodOrder);
            var interpolValues = _function.CalculateValues(calculatablePoints.GetXValues());
            GUIManipulator.AddLineToGraph(graphByPoints, interpolValues, "Interpolated " + exp.Expression, _interpolDataPointStyle);
        }

        private void BuildExpandedGraphic(ExpressionContainer exp, int additionalPointsNumber)
        {
            Points naturalPoints;
            Points interpolationPoints;

            GetExpandedPoints(exp, additionalPointsNumber, out naturalPoints, out interpolationPoints);

            GUIManipulator.AddLineToGraph(naturalGraphic, naturalPoints, exp.Expression, _natDataPointStyle);
            GUIManipulator.AddLineToGraph(naturalGraphic, interpolationPoints, "Interpolated " + exp.Expression, _interpolDataPointStyle);

            if (exp.LoadedFromFile)
                tabsControl.Items.Remove(mistakeGraphItem);
            else
                BuildMistakeGraphic(naturalPoints, interpolationPoints);
        }

        private void GetExpandedPoints(ExpressionContainer initialContainer, int additionalPointsNumber,
            out Points naturalPoints, out Points interpolationPoints)
        {
            if (!initialContainer.LoadedFromFile)
            {
                var expandedContainer =
                    new ExpressionContainer(
                        initialContainer.Expression,
                        initialContainer.VariableName,
                        initialContainer.IntervalStart,
                        initialContainer.IntervalStop,
                        initialContainer.Grid.Count * additionalPointsNumber,
                        initialContainer.MethodOrder);
                naturalPoints = GetCalculateablePointsList(
                    expandedContainer.Grid.Values.PointList,
                    initialContainer.MethodOrder * additionalPointsNumber);

                interpolationPoints = _function.CalculateValues(naturalPoints.GetXValues());
            }
            else
            {
                naturalPoints = GetCalculateablePointsList(initialContainer.Grid.Values.PointList, initialContainer.MethodOrder);
                interpolationPoints = _function.CalculateValues(naturalPoints.GetXValues());
            }
        }

        private void BuildMistakeGraphic(Points naturalValues, Points interpolValues)
        {
            Points mistakePoints = new Points();
            for (var i = 0; i < naturalValues.Count; ++i)
            {
                var x = naturalValues[i].X;
                var y = Math.Abs(naturalValues[i].Y - interpolValues[i].Y);
                mistakePoints.Add(new Point(x, y));
            }
            GUIManipulator.AddLineToGraph(mistakeGraphic,mistakePoints,"Mistake",_interpolDataPointStyle);
        }

        private Points GetCalculateablePointsList(List<Point> points, int order)
        {
            var numberOfPointsInRange = points.Count - (order) * 4;
            return new Points(points.GetRange(order*2, numberOfPointsInRange));
        }

        #endregion
    }
}
